/**
 * @file tcp_server_app.c
 * @brief TcpApp/TCP server
 * @Author: aple
 * @Date: 2025-9-27 9:20:10
 * @LastEditors: aple
 * @LastEditTime: 2025-9-27 18:37:50
 * @Verision: 1.0.0.0
 **/

// Switch to the appropriate trace level
#define TRACE_LEVEL TCP_APP_TRACE_LEVEL

// Dependencies
#include "server/tcp_server_app.h"
#include "server/tcp_server_app_transport.h"
#include "server/tcp_server_app_misc.h"
#include "debug.h"

/**
 * @brief Initialize settings with default values
 * @param[out] settings Structure that contains TcpApp/TCP server settings
 **/

void TcpAppServerGetDefaultSettings(TcpAppServerSettings *settings)
{
   // Default task parameters
   settings->task = OS_TASK_DEFAULT_PARAMS;
   settings->task.stackSize = TCP_APP_SERVER_STACK_SIZE;
   settings->task.priority = TCP_APP_SERVER_PRIORITY;

   // The TcpApp/TCP server is not bound to any interface
   settings->interface = NULL;

   // TcpApp/TCP port number
   settings->port = TCP_APP_TCP_SERVER_PORT;
   // Idle connection timeout
   settings->timeout = TCP_APP_SERVER_TIMEOUT;

   // TCP connection open callback function
   settings->openCallback = NULL;
   // TCP connection close callback function
   settings->closeCallback = NULL;

   // Lock TcpApp table callback function
   settings->lockCallback = NULL;
   // Unlock TcpApp table callback function
   settings->unlockCallback = NULL;
   // PDU processing callback
   settings->processAduCallback = NULL;
   // Tick callback function
   settings->tickCallback = NULL;
}

/**
 * @brief Initialize TcpApp/TCP server context
 * @param[in] context Pointer to the TcpApp/TCP server context
 * @param[in] settings TcpApp/TCP server specific settings
 * @return Error code
 **/

error_t TcpAppServerInit(TcpAppServerContext *context,
                         const TcpAppServerSettings *settings)
{
   error_t error;

   // Debug message
   TRACE_INFO("Initializing TcpApp/TCP server...\r\n");

   // Ensure the parameters are valid
   if (context == NULL || settings == NULL)
      return ERROR_INVALID_PARAMETER;

   // Clear TcpApp/TCP server context
   osMemset(context, 0, sizeof(TcpAppServerContext));

   // Initialize task parameters
   context->taskParams = settings->task;
   context->taskId = OS_INVALID_TASK_ID;

   // Save user settings
   context->settings = *settings;

   // Initialize status code
   error = NO_ERROR;

   // Create an event object to poll the state of sockets
   if (!osCreateEvent(&context->event))
   {
      // Failed to create event
      error = ERROR_OUT_OF_RESOURCES;
   }

   // Any error to report?
   if (error)
   {
      // Clean up side effects
      TcpAppServerDeinit(context);
   }

   // Return status code
   return error;
}

/**
 * @brief Start TcpApp/TCP server
 * @param[in] context Pointer to the TcpApp/TCP server context
 * @return Error code
 **/

error_t TcpAppServerStart(TcpAppServerContext *context)
{
   error_t error;

   // Make sure the TcpApp/TCP server context is valid
   if (context == NULL)
      return ERROR_INVALID_PARAMETER;

   // Debug message
   TRACE_INFO("Starting TcpApp/TCP server...\r\n");

   // Make sure the TcpApp/TCP server is not already running
   if (context->running)
      return ERROR_ALREADY_RUNNING;

   // Start of exception handling block
   do
   {
      // Open a TCP socket
      context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
      // Failed to open socket?
      if (context->socket == NULL)
      {
         // Report an error
         error = ERROR_OPEN_FAILED;
         break;
      }

      // Force the socket to operate in non-blocking mode
      error = socketSetTimeout(context->socket, 0);
      // Any error to report?
      if (error)
         break;

      // Associate the socket with the relevant interface
      error = socketBindToInterface(context->socket,
                                    context->settings.interface);
      // Any error to report?
      if (error)
         break;

      // The TcpApp/TCP server listens for connection requests on port 502
      error = socketBind(context->socket, &IP_ADDR_ANY, context->settings.port);
      // Any error to report?
      if (error)
         break;

      // Place socket in listening state
      error = socketListen(context->socket, 0);
      // Any error to report?
      if (error)
         break;

      // Start the TcpApp/TCP server
      context->stop = FALSE;
      context->running = TRUE;

      // Create a task
      context->taskId = osCreateTask("TcpApp/TCP Server", (OsTaskCode)TcpAppServerTask, context, &context->taskParams);

      // Failed to create task?
      if (context->taskId == OS_INVALID_TASK_ID)
      {
         // Report an error
         error = ERROR_OUT_OF_RESOURCES;
         break;
      }

      // End of exception handling block
   } while (0);

   // Any error to report?
   if (error)
   {
      // Clean up side effects
      context->running = FALSE;

      // Close listening socket
      socketClose(context->socket);
      context->socket = NULL;
   }

   // Return status code
   return error;
}

/**
 * @brief Stop TcpApp/TCP server
 * @param[in] context Pointer to the TcpApp/TCP server context
 * @return Error code
 **/

error_t TcpAppServerStop(TcpAppServerContext *context)
{
   uint_t i;

   // Make sure the TcpApp/TCP server context is valid
   if (context == NULL)
      return ERROR_INVALID_PARAMETER;

   // Debug message
   TRACE_INFO("Stopping TcpApp/TCP server...\r\n");

   // Check whether the TcpApp/TCP server is running
   if (context->running)
   {
#if (NET_RTOS_SUPPORT == ENABLED)
      // Stop the TcpApp/TCP server
      context->stop = TRUE;
      // Send a signal to the task to abort any blocking operation
      osSetEvent(&context->event);

      // Wait for the task to terminate
      while (context->running)
      {
         osDelayTask(1);
      }
#endif

      // Loop through the connection table
      for (i = 0; i < TCP_APP_SERVER_MAX_CONNECTIONS; i++)
      {
         // Check the state of the current connection
         if (context->connection[i].state != TCP_APP_CONNECTION_STATE_CLOSED)
         {
            // Close client connection
            TcpAppServerCloseConnection(&context->connection[i]);
         }
      }

      // Close listening socket
      socketClose(context->socket);
      context->socket = NULL;
   }

   // Successful processing
   return NO_ERROR;
}

/**
 * @brief TcpApp/TCP server task
 * @param[in] context Pointer to the TcpApp/TCP server context
 **/

void TcpAppServerTask(TcpAppServerContext *context)
{
   error_t error;
   uint_t i;
   systime_t timeout;
   TcpAppClientConnection *connection;
   SocketEventDesc eventDesc[TCP_APP_SERVER_MAX_CONNECTIONS + 1];

#if (NET_RTOS_SUPPORT == ENABLED)
   // Task prologue
   osEnterTask();

   // Process events
   while (1)
   {
#endif
      // Set polling timeout
      timeout = TCP_APP_SERVER_TICK_INTERVAL;

      // Clear event descriptor set
      osMemset(eventDesc, 0, sizeof(eventDesc));

      // Specify the events the application is interested in
      for (i = 0; i < TCP_APP_SERVER_MAX_CONNECTIONS; i++)
      {
         // Point to the structure describing the current connection
         connection = &context->connection[i];

         // Loop through active connections only
         if (connection->state != TCP_APP_CONNECTION_STATE_CLOSED)
         {
            // Register connection events
            TcpAppServerRegisterConnectionEvents(connection, &eventDesc[i]);

            // Check whether the socket is ready for I/O operation
            if (eventDesc[i].eventFlags != 0)
            {
               // No need to poll the underlying socket for incoming traffic
               timeout = 0;
            }
         }
      }

      // The TcpApp/TCP server listens for connection requests on port 502
      eventDesc[i].socket = context->socket;
      eventDesc[i].eventMask = SOCKET_EVENT_RX_READY;

      // Wait for one of the set of sockets to become ready to perform I/O
      error = socketPoll(eventDesc, TCP_APP_SERVER_MAX_CONNECTIONS + 1,
                         &context->event, timeout);

      // Check status code
      if (error == NO_ERROR || error == ERROR_TIMEOUT ||
          error == ERROR_WAIT_CANCELED)
      {
         // Stop request?
         if (context->stop)
         {
            // Stop TcpApp/TCP server operation
            context->running = FALSE;
            // Task epilogue
            osExitTask();
            // Kill ourselves
            osDeleteTask(OS_SELF_TASK_ID);
         }

         // Event-driven processing
         for (i = 0; i < TCP_APP_SERVER_MAX_CONNECTIONS; i++)
         {
            // Point to the structure describing the current connection
            connection = &context->connection[i];

            // Loop through active connections only
            if (connection->state != TCP_APP_CONNECTION_STATE_CLOSED)
            {
               // Check whether the socket is ready to perform I/O
               if (eventDesc[i].eventFlags != 0)
               {
                  // Connection event handler
                  TcpAppServerProcessConnectionEvents(connection);
               }
            }
         }

         // Any connection request received on port 502?
         if (eventDesc[i].eventFlags != 0)
         {
            // Accept connection request
            TcpAppServerAcceptConnection(context);
         }
      }

      // Handle periodic operations
      TcpAppServerTick(context);

#if (NET_RTOS_SUPPORT == ENABLED)
   }
#endif
}

/**
 * @brief Release TcpApp/TCP server context
 * @param[in] context Pointer to the TcpApp/TCP server context
 **/

void TcpAppServerDeinit(TcpAppServerContext *context)
{
   // Make sure the TcpApp/TCP server context is valid
   if (context != NULL)
   {
      // Free previously allocated resources
      osDeleteEvent(&context->event);

      // Clear TcpApp/TCP server context
      osMemset(context, 0, sizeof(TcpAppServerContext));
   }
}
